# 引入django中表单的模块
from django import forms
# 引入models中的类
from web.models import UserInfo
# 引入异常
from django.core.exceptions import ValidationError
# 引入正则模块
import re
# 引入settings
from django.conf import settings
# 引入随机值函数
import random
# 引入发腾讯短信接口
from utils.tencent.sms import send_sms_single
# 引入Redis模块
from django_redis import get_redis_connection
# 引入md5加密模块
from utils import encrypt
# 引入Q查询
from django.db.models import Q

"""

#  新建一个用户注册的表单
class RegisterForm(forms.Form):
    # 登录名，用户名，密码，重复密码，邮箱，手机号码，验证
    login_name = forms.CharField(label="登录名", widget=forms.TextInput(attrs={'class': 'form-control',
                                                                            'placeholder': '请输入登录名'
                                                                            }))
    username = forms.CharField(label="用户名", widget=forms.TextInput(attrs={'class': 'form-control',
                                                                          'placeholder': '请输入用户名'
                                                                          }))
    password = forms.CharField(label="密码", widget=forms.PasswordInput(attrs={'class': 'form-control',
                                                                             'placeholder': '请输入邮箱'
                                                                             }))
    re_password = forms.CharField(label="确认密码", widget=forms.PasswordInput(attrs={'class': 'form-control',
                                                                                  'placeholder': '请输入邮箱'
                                                                                  }))
    email = forms.EmailField(label="邮箱", widget=forms.TextInput(attrs={'class': 'form-control',
                                                                       'placeholder': '请输入邮箱'
                                                                       }))
    mobile_phone = forms.CharField(label="手机号码",
                                   widget=forms.TextInput(attrs={'class': 'form-control',
                                                                 'placeholder': '请输入手机号码'
                                                                 })
                                   )
    code = forms.CharField(label="验证码", widget=forms.TextInput(attrs={'class': 'form-control',
                                                                        'placeholder': '请输入验证码'
                                                                      }))
"""


# 用户注册表单
class RegisterForm(forms.ModelForm):
    # 添加字段
    password = forms.CharField(label="密码", required=True, widget=forms.PasswordInput())
    re_password = forms.CharField(label="确认密码", required=True, widget=forms.PasswordInput())
    code = forms.CharField(label="验证码", required=True, )

    class Meta:
        model = UserInfo
        fields = ['login_name', 'username', 'password', 're_password', 'email', 'mobile_phone', 'code']

    # 重新类的初始化函数
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 为当前所有字段添加属性
        for name, field in self.fields.items():
            # 添加class: form-control
            field.widget.attrs['class'] = "form-control"
            # 添加placeholder属性
            field.widget.attrs['placeholder'] = "请输入{}".format(field.label)

    # ===================钩子函数实现=======================
    def clean_login_name(self):
        # 取出值
        login_name = self.cleaned_data['login_name']
        # 判断在数据库中是否存在
        is_exists = UserInfo.objects.filter(login_name=login_name).exists()
        if is_exists:
            raise ValidationError("登陆名已存在！")

        # 返回
        return login_name

    def clean_password(self):
        # 取出password -- 明文
        password = self.cleaned_data['password']
        # 长度是否介于8-20
        if len(password) < 8 or len(password) > 20:
            raise ValidationError("密码长度必须介于8-20位！")
        # 返回
        return encrypt.md5(password)

    def clean_re_password(self):
        # 取出确认密码 --- 明文
        re_password = self.cleaned_data['re_password']
        password = self.cleaned_data['password']

        encrypt_re_password = encrypt.md5(re_password)
        # 判断两次密码是否一致
        if password != encrypt_re_password:
            raise ValidationError("两次密码不一致，请重新输入！")
        # 返回
        return re_password

    def clean_email(self):
        # 取出邮箱
        email = self.cleaned_data['email']
        # 判断在数据库是否存在
        is_exists = UserInfo.objects.filter(email=email).exists()
        if is_exists:
            raise ValidationError("邮箱地址已存在！")
        return email

    def clean_mobile_phone(self):
        # 取出手机号码
        mobile_phone = self.cleaned_data['mobile_phone']
        # ===正则表达式===
        match_res = re.match(r'^[1][345789]\d{9}$', mobile_phone)
        # 如果不匹配
        if not match_res:
            raise ValidationError("手机号码不符合规范！")
        # 判断在数据库是否存在
        is_exists = UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if is_exists:
            raise ValidationError("手机号码已存在！")
        # 返回
        return mobile_phone

    def clean_code(self):
        # 取出验证码
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data['mobile_phone']
        # 在redis中取出code
        conn = get_redis_connection()
        # 取出code
        redis_code = conn.get(mobile_phone)
        # ===判断redis_code是否存在
        if not redis_code:
            raise ValidationError("验证码已过期!请重新发送！")
        # =====判断验证码一致-----
        if code.strip() != redis_code.decode('utf-8'):
            raise ValidationError("输入的验证码错误！")
        # 返回
        return code


# 登陆表单
class LoginForm(forms.Form):
    login_name = forms.CharField(label="用户名/邮箱/手机号码", required=True)
    password = forms.CharField(label="密码", widget=forms.PasswordInput(render_value=True))
    image_code = forms.CharField(label="图片验证码")

    # 重新类的初始化函数
    def __init__(self, request, *args, **kwargs):
        # 使用父类初始化
        super().__init__(*args, **kwargs)
        # 接收views中传递来的request
        self.request = request
        # 为当前所有字段添加属性
        for name, field in self.fields.items():
            # 添加class: form-control
            field.widget.attrs['class'] = "form-control"
            # 添加placeholder属性
            field.widget.attrs['placeholder'] = "请输入{}".format(field.label)

    # ================钩子函数======================
    def clean_password(self):
        # 获取密码
        password = self.cleaned_data['password']
        # 返回加密的密码
        return encrypt.md5(password)

    def clean_image_code(self):
        # 获取输入的code
        image_code = self.cleaned_data['image_code']
        # 获取用户名和密码
        login_name = self.cleaned_data['login_name']
        password = self.cleaned_data['password']

        # 校验图片验证码输入的是否正确
        session_code = self.request.session.get('image_code')
        # 判断是否有验证码
        if not session_code:
            raise ValidationError("验证码已过期！请重新获取！")
        # 判断验证码是否正确
        if image_code.strip().upper() != session_code.strip().upper():
            raise ValidationError("验证码输入错误！")

        # ==================== 身份验证 =================
        # user_object = UserInfo.objects.filter(
        #     Q(login_name=login_name) | Q(email=login_name) | Q(mobile_phone=login_name)).filter(
        #     password=password).first()
        #
        # # 判断是否有对象
        # if not user_object:
        #     raise ValidationError("用户名或者密码错误！")

        # 返回
        return image_code


# 手机验证码登录表单
class SmsLoginForm(forms.Form):
    mobile_phone = forms.CharField(label="手机号码")
    code = forms.CharField(label="验证码")

    # 重新类的初始化函数
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 为当前所有字段添加属性
        for name, field in self.fields.items():
            # 添加class: form-control
            field.widget.attrs['class'] = "form-control"
            # 添加placeholder属性
            field.widget.attrs['placeholder'] = "请输入{}".format(field.label)

    # ===================钩子函数 ================
    def clean_mobile_phone(self):
        # 获取手机号码
        mobile_phone = self.cleaned_data['mobile_phone']
        # 判断是否存在
        user_object = UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not user_object:
            raise ValidationError("手机号码不存在，请先注册!")
        # 返回
        return user_object

    def clean_code(self):
        # 获取验证码
        code = self.cleaned_data['code']
        user_object = self.cleaned_data['mobile_phone']
        # 判断
        if not user_object:
            return code
        # ===========验证码校验=================
        conn = get_redis_connection()
        redis_code = conn.get(user_object.mobile_phone)
        # 如果没有取到
        if not redis_code:
            raise ValidationError("验证码失效或者未发送，请重新发送！")
        # 把验证码转为字符串
        redis_code_str = redis_code.decode("utf-8")
        # 比较
        if code.strip() != redis_code_str:
            raise ValidationError("验证码错误！重新输入！")

        # 返回
        return code


# 重置密码的表单
class ResetPasswordForm(forms.Form):
    mobile_phone = forms.CharField(label="手机号码", required=True)
    code = forms.CharField(label="验证码", required=True)
    password = forms.CharField(label="密码", required=True, widget=forms.PasswordInput())
    re_password = forms.CharField(label="重复密码", required=True, widget=forms.PasswordInput())

    # 重新类的初始化函数
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 为当前所有字段添加属性
        for name, field in self.fields.items():
            # 添加class: form-control
            field.widget.attrs['class'] = "form-control"
            # 添加placeholder属性
            field.widget.attrs['placeholder'] = "请输入{}".format(field.label)

    # ========================= 钩子函数=============================
    def clean_mobile_phone(self):
        # 获取手机号码
        mobile_phone = self.cleaned_data.get('mobile_phone')
        # ===正则表达式===
        match_res = re.match(r'^[1][345789]\d{9}$', mobile_phone)
        # 如果不匹配
        if not match_res:
            raise ValidationError("手机号码不符合规范！")
        # =====判断数据库是否存在===！
        user_object = UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not user_object:
            raise ValidationError("手机号码不存在!")
        # 返回
        return user_object

    def clean_code(self):
        # 获取验证码
        code = self.cleaned_data.get('code')
        user_object = self.cleaned_data.get('mobile_phone')
        # 判断code是否存在
        if not user_object:
            return code
        # =======验证码比较=========
        conn = get_redis_connection()
        redis_code = conn.get(user_object.mobile_phone)
        # 判断是否存在
        if not redis_code:
            raise ValidationError("验证码未发送或者已过期，请重新发送！")
        # redis_code转为str
        redis_code_str = redis_code.decode('utf-8')
        # 比较
        if code.strip() != redis_code_str:
            raise ValidationError("验证码输入错误！")
        # 返回
        return code

    def clean_password(self):
        # 获取password
        password = self.cleaned_data.get('password')
        # 判断
        if len(password)<8 or len(password)>20:
            raise ValidationError('密码长度要求8-20位！')
        # 返回密文
        return encrypt.md5(password)

    def clean_re_password(self):
        # 获取确认的密码
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        # 判断
        if password != encrypt.md5(re_password):
            raise ValidationError('两次密码输入不同！')
        # 返回
        return re_password


# 用户测试表单
class DemoForm(forms.ModelForm):
    # 添加字段
    login_name = forms.CharField(label="登录名", required=True, min_length=4, max_length=8,
                                 error_messages={
                                     "min-length": '密码最短4位！',
                                     "max_length": '密码最长8位！'
                                 })
    password = forms.CharField(label="密码", widget=forms.PasswordInput())
    re_password = forms.CharField(label="确认密码", widget=forms.PasswordInput())

    class Meta:
        model = UserInfo
        fields = ['login_name', 'username', 'password', 're_password', 'email', 'mobile_phone']

    # 重新类的初始化函数
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 为当前所有字段添加属性
        for name, field in self.fields.items():
            # 添加class: form-control
            field.widget.attrs['class'] = "form-control"
            # 添加placeholder属性
            field.widget.attrs['placeholder'] = "请输入{}".format(field.label)

    # 额外校验：注册登录名的时候， 不允许添加root, admin, adminstrator
    # ================== 钩子函数 =============
    def clean_login_name(self):
        # 取出已经校验的值
        login_name = self.cleaned_data['login_name']
        # 判断
        if login_name in ['root', 'admin', 'administrator']:
            # 异常
            raise ValidationError("输入的登录名是系统保留！请换一个！")
        # 返回
        return login_name


# 用户发送短信表单--- 验证
class SmsSendForm(forms.Form):
    mobile_phone = forms.CharField(required=True)

    # 重写form表单__init__
    def __init__(self, request, *args, **kwargs):
        # 传给父类做初始化
        super().__init__(*args, **kwargs)
        # 接收request
        self.request = request

    # 手机号码： 1. 符合正则表达式   2. 在数据库中是否存在
    # =================钩子函数====================
    def clean_mobile_phone(self):
        # 获取手机号码
        mobile_phone = self.cleaned_data['mobile_phone']
        # 获取tpl的字符串
        tpl_str = self.request.POST.get('tpl')
        # 判断---tpl的字符串是否正确 --- 是否能取到短信模板的id
        tpl_id = settings.TENCENT_SMS['templates'].get(tpl_str)
        # 如果取不到，报错
        if not tpl_id:
            raise ValidationError('短信模板编号无法获取！')

        # 判断 ---- 手机号码是否符合格式规范-- 11位数字，第一位1，--- 正则表达式
        if not re.match(r'^[1][345789]\d{9}$', mobile_phone):
            raise ValidationError('手机号码不符合规范！')

        # 判断 ---- 注册--- 数据库中要没有
        is_exsits = UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        # 判断是否存在
        if tpl_str == "register":
            if is_exsits:
                raise ValidationError('手机号码已注册！')
        else:  # login , reset
            if not is_exsits:
                raise ValidationError('手机号码不存在！')

        # ================= 发送验证码 ===============
        """
        # 准备开发发送短信
        code = random.randint(1000, 9999)
        # 开始发送
        tencent_res = send_sms_single(mobile_phone, tpl_id, [code, ])
        # 判断短信是否发送成功
        if tencent_res['result'] != 0:
            # 展示腾讯返回的错误
            raise ValidationError('短信发送异常！具体原因：%s' % (tencent_res['errmsg']))

        # ===== 发送成功 ======
        # 把验证码写入 redis  13482034096: code
        # 获取一个连接
        conn = get_redis_connection("default")
        # 写入一个记录
        conn.set(mobile_phone, code, ex=60)
        """
        # 返回
        return mobile_phone
